/*
    基于多线程技术实现大文件的分卷处理，卷大小可以自己命令行输入以字节为单位
    每个分卷使用 SHA256 值命名
*/

#include <string.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <openssl/evp.h>
#include <openssl/sha.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

void* routine(void* avg);

typedef struct ret
{
    char* name;
    long post;  //位置
    char* file;
    int buffer;
}ret;

int main(int argc, char* argv[])
{
    // ./copy_thread 文件名 分卷大小 写入的文件
    if(argc !=  4)
    {
        perror("参数输入错误，请重新输入参数 eg : ./copy_thread 文件名 分卷大小 写入文件名\n");
        exit(1);
    }
    int splice = atoi(argv[2]);
    // 多线程同时向同一文件中进行数据修改操作，会数据竞争，所以将文件的名字返回，由主线程进行统一写入
    FILE* fp = fopen(argv[1], "r");
    fseek(fp, 0, SEEK_END);
    int size = ftell(fp);

    int num = (size + splice - 1) / splice;
    //循环迭代，分配任务
    pthread_t tid[num];
    ret rets[num];

    for(int i = 0; i < num; i++)
    {
        rets[i].post = splice*i;    // 0 1024 
        rets[i].file = argv[1];
        rets[i].buffer = splice;
        rets[i].name = (char*)malloc(SHA256_DIGEST_LENGTH * 2+1);

        pthread_create(&tid[i], NULL, routine, &rets[i]);
    }
    // 主线程执行写入操作
    FILE* fp_w = fopen(argv[3], "w");
    for(int i =0; i < num; i++)
    {
        pthread_join(tid[i], NULL);
        fwrite(rets[i].name, 1, strlen(rets[i].name), fp_w);
        fputc('\n', fp_w);
    }
    fclose(fp_w);
    fclose(fp);
    return 0;
}

void* routine(void* avg)
{
    ret* rets = (ret*) avg;
    FILE* fp = fopen(rets->file, "r");
    fseek(fp, rets->post, SEEK_CUR);
    char buffer[rets->buffer];
    int size = fread(buffer, 1, sizeof(buffer), fp);
    unsigned char blockInfo[SHA256_DIGEST_LENGTH];
    //创建上下文
    EVP_MD_CTX* blockctx = EVP_MD_CTX_new();
    //初始化算法
    EVP_DigestInit_ex(blockctx, EVP_sha256(), NULL);
    //设置数据
    EVP_DigestUpdate(blockctx, buffer, size);
    //获取摘要信息
    EVP_DigestFinal_ex(blockctx, blockInfo, NULL);

    for(int i = 0; i < SHA256_DIGEST_LENGTH; i++)
    {
        snprintf(&rets->name[i * 2], 3, "%02x", blockInfo[i]);
    }
    rets->name[SHA256_DIGEST_LENGTH * 2] = '\0'; // 确保字符串以 null 结尾
    FILE* fp_w = fopen(rets->name, "w");
    fwrite(buffer, 1, size, fp_w);
    fclose(fp_w);
}